//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <core.h>

#define MAXWRITEBYTES 16
#define MAXSAVECODE 16

typedef struct _REGINFO
{
    uint32_t  uZero;
    uint32_t  uAt;
    uint32_t  uV0;
    uint32_t  uV1;
    uint32_t  uA0;
    uint32_t  uA1;
    uint32_t  uA2;
    uint32_t  uA3;
    uint32_t  uT0;
    uint32_t  uT1;
    uint32_t  uT2;
    uint32_t  uT3;
    uint32_t  uT4;
    uint32_t  uT5;
    uint32_t  uT6;
    uint32_t  uT7;
    uint32_t  uS0;
    uint32_t  uS1;
    uint32_t  uS2;
    uint32_t  uS3;
    uint32_t  uS4;
    uint32_t  uS5;
    uint32_t  uS6;
    uint32_t  uS7;
    uint32_t  uT8;
    uint32_t  uT9;
    uint32_t  uK0;
    uint32_t  uK1;
    uint32_t  uGp;
    uint32_t  uSp;
    uint32_t  uS8;
    uint32_t  uRa;
} REGINFO, *PREGINFO;

typedef UInt32 ADDR_SIZE;
static Address saveaddr = 0;
static Address ChatFuncAddr;
static Byte savecode[MAXSAVECODE];
static Byte callcode[MAXSAVECODE];
const char AsciiTbl[]={'0','1','2','3','4','5','6',
    '7','8','9','a','b','c','d','e','f'};

static void __cdecl dputc(unsigned char c)
{
    if (c == '\n')
        BspWriteComPort(COMPORT_DEBUG, '\r');

    BspWriteComPort(COMPORT_DEBUG, c);
}

static void __cdecl dputw(UInt32 x)
{
    char c;
    int i;
    dputc('x');
    for (i = 28;i >= 0;i -= 4) {
        c = x >> i;
        dputc(AsciiTbl[c & 0x0f]);
    }
}

static void __cdecl dputw8(UInt8 x)
{
    UInt8 c;
    c = x >> 4;
    dputc(AsciiTbl[c & 0x0f]);
    dputc(AsciiTbl[x & 0x0f]);
}

static void dumpw(UInt32 *p, int nw)
{
    int i, j;
    int col = 4;
    unsigned char *pAscii;

    while (nw > 0) {
        dputw((UInt32)p);
        pAscii = (unsigned char *)p;
        i = 0;
        while (i++ < col && nw > 0) {
            dputc(' ');
            dputw(*p++);
            nw--;
            if (i == col) {
                j = 0;
                dputc(' ');
                do {
                    if ((*pAscii == 0)
                            || (*pAscii == 3)
                            || (*pAscii == 8)
                            || (*pAscii == 9)
                            || (*pAscii == 10)
                            || (*pAscii == 13))
                        dputc('.');
                    else
                        dputc(*pAscii);
                    pAscii++;
                    j++;
                } while (j < (col * 4));
                dputc('\n');
            }
        }
    }
    dputc('\n');
}

static void dumpb(Byte* data, UInt32 size)
{
    UInt32 i,j,k;
    char visdata[17];
    visdata[16] = '\0';
    for (i = 0;i < size;) {
        // print line head
        dputw((UInt32)data+i);
        dputc(' ');
        // print data
        for (k = 0;k < 16; i++, k++) {
            j = i & 0x0f; /* mod 16 */
            if (i < size) {
                dputw8(data[i]);
                dputc(' ');
                visdata[j] = 
                    (data[i] >= 0x20 && data[i] <= 0x80) ? data[i] : '.';
            }
            else {
                visdata[j] = '\0';
                dputc(' ');
                dputc(' ');
                dputc(' ');
            }
        }
        // print visible character
        dputc(' ');
        for (k = 0; k < 16; k++) dputc(visdata[k]);
        dputc('\n');
    }
}

static void DebugCmdError(void)
{
    kprintf("debug cmd error. <h> for help.\n");
}

static Address ReadAddress(char *pArg)
{
    Address p = 0;
    unsigned char c;

    while (*pArg == '0' || *pArg == 'x' || *pArg == 'X') pArg++;//skip [0x]

    c = *pArg++;
    while (c) {
        UInt32 digit;

        if (('0' <= c) && (c <= '9'))
            digit = c - '0';
        else if (('a' <= c) && (c <= 'f'))
            digit = c - 'a' + 10;
        else if (('A' <= c) && (c <= 'F'))
            digit = c - 'A' + 10;
        else
            break;
        p = (p << 4) | digit;

        c = *pArg++;
    }

    return p;
}

static char *GetFirstArg(char *pcmd)
{
    // ignore the command
    //
    while (*pcmd) {
        if (' ' == *pcmd || 0 == *pcmd)
            break;
        pcmd++;
    }

    // find the first argument begin
    //
    while (*pcmd) {
        if (' ' != *pcmd && 0 != *pcmd)
            break;
        pcmd++;
    }

    return pcmd;
}

static unsigned char __cdecl dp_getc2(void)
{
    unsigned char c;

    while (!BspReadComPort(COMPORT_DEBUG, &c));

    return c;
}

static int dgets2(unsigned char *buf, int lim)
{
    int ret = 0;
    unsigned char c;

    while (lim > ret) {
        c = dp_getc2();
        dputc(c);
        if (c == '\n' || c == '\r')
            break;
        *buf++ = c;
        ret++;
    }

    *buf = 0;
    return ret;
}

static void RestoreCode(PCXTINFO pCxt)
{
    kprintf("pass breakpoint %x\n", saveaddr);
    memcpy((void *)saveaddr, savecode, MAXSAVECODE);
    ((PREGINFO)(pCxt->regs))->uRa = ((PREGINFO)(pCxt->regs))->uRa - 8;
    saveaddr = 0;
}

////////////////////////////////////////////////////////////////////

static Address __cdecl GetChatFuncAddr()
{
    return (Address)Chat;
}

EXTERN_C UInt32 ChatInit()
{
    ChatFuncAddr = GetChatFuncAddr();

    return 0;
}

EXTERN_C void myChat(PCXTINFO pCxt)
{
    PREGINFO pReg = (PREGINFO)&pCxt->regs[0];

    // note dputc, dgetc use unsigned char, not wchar_t
    //
    unsigned char   cmd[128];
    uint8_t         i;
    uint8_t         j;

    dputc('\n');

    // maybe came from breakpoint, restore memory
    if (saveaddr) {
        RestoreCode(pCxt);
    }

    while (1) {
        memset(cmd, 0, sizeof(cmd));
        kprintf("in kernel debug:");
        dgets2(cmd, sizeof(cmd));
        dputc('\n');

        // Copy up to first space of cmd string
        //
        i = 0;
        j = 0;
        while ((cmd[i] == ' ') || (cmd[i] == 8)) {
            i++;
        }

        if (i > 0) {
            while (i < sizeof(cmd)) {
                cmd[j] = cmd[i];
                i++;
                j++;
            }
        }

        // clear <backspace> - key
        //
        i = 0;
        while (cmd[i++] != 0) {
            if (cmd[i] == 8) {
                for (j = i; cmd[j] != 0; j++)
                    cmd[j-1] = cmd[j+1];
                cmd[j-1] = 0;
                i = i-2;
            }
        }

        switch (cmd[0]) {
            case 'd':{
                char *pAddress,*pSize;
                Address p;
                ADDR_SIZE s;

                pAddress = GetFirstArg((char *)cmd);
                if (*pAddress == 0) {
                    DebugCmdError();
                    break;
                }

                pSize = GetFirstArg(pAddress);
                if (*pSize == 0) {
                    pSize = "10";
                }

                p= ReadAddress(pAddress);
                s = ReadAddress(pSize);
                kprintf("\\rd: Address=%x, Size=%x\n",p,s);
                switch (cmd[1]) {
                    case 'd':
                        dumpw((UInt32 *)p,(int)(s >> 2));
                        break;
                    default:
                        dumpb((Byte *)p, s);
                }
                break;
            }
            case 'r':{
                char *pAddress;
                pAddress = GetFirstArg((char *)cmd);

                switch (*pAddress) {
                    case 'a':
                        if (*(pAddress + 2) != ' ') {
                            break;
                        }
                        switch (*(pAddress + 1)) {
                            case '0':
                                pReg->uA0 = ReadAddress(pAddress + 3);
                                break;
                            case '1':
                                pReg->uA1 = ReadAddress(pAddress + 3);
                                break;
                            case '2':
                                pReg->uA2 = ReadAddress(pAddress + 3);
                                break;
                            case '3':
                                pReg->uA3 = ReadAddress(pAddress + 3);
                                break;
                            case 't':
                                pReg->uAt = ReadAddress(pAddress + 3);
                                break;
                            default:;
                        }

                        break;
                    case 'v':
                        if (*(pAddress + 2) != ' ') {
                            break;
                        }
                        switch (*(pAddress + 1)) {
                            case '0':
                                pReg->uV0 = ReadAddress(pAddress + 3);
                                break;
                            case '1':
                                pReg->uV1 = ReadAddress(pAddress + 3);
                                break;
                            default:;
                        }

                        break;

                    case 't':
                        if (*(pAddress + 2) != ' ') {
                            break;
                        }
                        switch (*(pAddress + 1)) {
                            case '0':
                                pReg->uT0 = ReadAddress(pAddress + 3);
                                break;
                            case '1':
                                pReg->uT1 = ReadAddress(pAddress + 3);
                                break;
                            case '2':
                                pReg->uT2 = ReadAddress(pAddress + 3);
                                break;
                            case '3':
                                pReg->uT3 = ReadAddress(pAddress + 3);
                                break;
                            case '4':
                                pReg->uT4 = ReadAddress(pAddress + 3);
                                break;
                            case '5':
                                pReg->uT5 = ReadAddress(pAddress + 3);
                                break;
                            case '6':
                                pReg->uT6 = ReadAddress(pAddress + 3);
                                break;
                            case '7':
                                pReg->uT7 = ReadAddress(pAddress + 3);
                                break;
                            case '8':
                                pReg->uT8 = ReadAddress(pAddress + 3);
                                break;
                            case '9':
                                pReg->uT9 = ReadAddress(pAddress + 3);
                                break;
                            default:;
                        }

                        break;

                    case 's':
                        if (strcmp(pAddress, "status")
                                && *(pAddress + 6) == ' ') {
                            pCxt->cp0_status = ReadAddress(pAddress + 7);
                            break;
                        }

                        if (*(pAddress + 2) != ' ') {
                            break;
                        }

                        switch (*(pAddress + 1)) {
                            case '0':
                                pReg->uS0 = ReadAddress(pAddress + 3);
                                break;
                            case '1':
                                pReg->uS1 = ReadAddress(pAddress + 3);
                                break;
                            case '2':
                                pReg->uS2 = ReadAddress(pAddress + 3);
                                break;
                            case '3':
                                pReg->uS3 = ReadAddress(pAddress + 3);
                                break;
                            case '4':
                                pReg->uS4 = ReadAddress(pAddress + 3);
                                break;
                            case '5':
                                pReg->uS5 = ReadAddress(pAddress + 3);
                                break;
                            case '6':
                                pReg->uS6 = ReadAddress(pAddress + 3);
                                break;
                            case '7':
                                pReg->uS7 = ReadAddress(pAddress + 3);
                                break;
                            case '8':
                                pReg->uS8 = ReadAddress(pAddress + 3);
                                break;
                            case 'p':
                                pReg->uSp = ReadAddress(pAddress + 3);
                                break;
                            default:;
                        }

                        break;
                    case 'k':
                        if (*(pAddress + 2) != ' ') {
                            break;
                        }
                        switch (*(pAddress + 1)) {
                            case '0':
                                pReg->uK0 = ReadAddress(pAddress + 3);
                                break;
                            case '1':
                                pReg->uK1 = ReadAddress(pAddress + 3);
                                break;
                            default:;
                        }

                        break;
                    case 'r':
                        if (*(pAddress + 1) == 'a'
                                && *(pAddress + 2) == ' ') {
                            pReg->uRa = ReadAddress(pAddress + 3);
                        }

                        break;
                    case 'g':
                        if (*(pAddress + 1) == 'p'
                                && *(pAddress + 2) == ' ') {
                            pReg->uGp = ReadAddress(pAddress + 3);
                        }

                        break;
                    case 'e':
                        if (*(pAddress + 1) == 'p'
                            && *(pAddress + 2) == 'c'
                            && *(pAddress + 3) == ' ') {
                            pCxt->cp0_epc = ReadAddress(pAddress + 4);
                        }

                        break;
                    default:;
                }
                DumpCxt(pCxt);
                break;
            }
            case 'w':{
                char    *pAddress,*pValue;
                Address p;
                Byte    v[MAXWRITEBYTES];
                int     s = 0;

                pValue = pAddress = GetFirstArg((char *)cmd);
                if (*pAddress == 0) {
                    DebugCmdError();
                    break;
                }

                p= ReadAddress(pAddress);

                do {
                    pValue = GetFirstArg(pValue);
                    if (*pValue == 0) break;
                // command maybe start with 'wd', 'wb'
                    switch (cmd[1]) {
                        case 'b':
                            *(Byte*)(v+s) = (Byte)ReadAddress(pValue);
                            s+=sizeof(Byte);
                            break;
                        default:
                            *(UInt32*)(v+s) = ReadAddress(pValue);
                            s+=sizeof(UInt32);
                            break;
                    }
                } while (s < MAXWRITEBYTES);

                kprintf("\\w: Address=%x, Size=%x\n",p,s);
                memcpy((void *)p, v, s);

                dma_cache_wback_inv(p, s);

                break;
            }
            case 'e':{
                char *pAddress;

                pAddress = GetFirstArg((char *)cmd);
                if (*pAddress == 0) {
                    if (saveaddr) {//default 'e' restore memory
                        memcpy((void *)saveaddr, savecode, MAXSAVECODE);
                        kprintf("disable the breakpoint at %x\n", saveaddr);
                        saveaddr = 0;
                    }
                    else {
                        kprintf("No breakpointer clear.\n");
                        DebugCmdError();
                    }
                    break;
                }

                Address taddr = ReadAddress(pAddress);
                if (saveaddr == taddr) {
                    break;
                }
                else if (saveaddr) {
                    memcpy((void *)saveaddr, savecode, MAXSAVECODE);
                    kprintf("disable the breakpoint at %x\n", saveaddr);
                    saveaddr = taddr;
                }
                else {
                    saveaddr = taddr;
                }

                // jal instruction for mips absolutely jump link
            //
                *(UInt32 *)(callcode) =
                    0x0c000000 | ((ChatFuncAddr & 0x0fffffff) >> 2);

                memcpy(savecode, (void *)saveaddr, MAXSAVECODE);
                memcpy((void *)saveaddr, callcode, MAXSAVECODE);

                kprintf("enable breakpoint at %x\n", saveaddr);

                break;
            }
            case 'b':{
                char *pAddress;

                pAddress = GetFirstArg((char *)cmd);

                if (*pAddress == 0) {
                    if (saveaddr) {//default 'b' restore memory
                        *(Byte*)saveaddr = savecode[0];
                        kprintf("disable breakpoint at %x\n", saveaddr);
                        saveaddr = 0;
                    }
                    else {
                        DebugCmdError();
                    }
                    break;
                }

                if (saveaddr) {
                    kprintf(
                        "sorry! i had got a breakpoint at %x\n", saveaddr);
                    break;
                }

                saveaddr= ReadAddress(pAddress);

                savecode[0] = *(Byte*)saveaddr;
                *(Byte*)saveaddr = 0xcc;

                kprintf("enable breakpoint at %x\n", saveaddr);

                break;
            }
            case 'q':
                return;
            default:
                kprintf("  \n");
                kprintf("  d <addr> <length>  Dump memory\n");
                kprintf("  w <addr> <value list>  Write memory\n");
                kprintf("  e <addr>     Make breakpoint\n");
                kprintf("  r            Read/Write context\n");
                kprintf("  p            Dump pv list\n");
                kprintf("  s            Scheduler state\n");
                kprintf("  k <ebp>      Stack trace\n");
                kprintf("  T[s|c] <thd> Thread state / context\n");
                kprintf("  ^L           PIC/RTC state\n");
        }
    }
}
